/*
 *
 *
 */

#include "user_common.h"
#include "user_stash.h"
#include "user_fingerprint.h"
#include "user_password.h"
#include "user_card.h"
#if defined(GKT_CONFIG_FACE_ID_ENABLE) && GKT_CONFIG_FACE_ID_ENABLE
#include "user_face_id.h"
#include "user_palm.h"
#endif

user_s g_user = {
	.instances = {
#if defined(GKT_CONFIG_FPS_ENABLE) && GKT_CONFIG_FPS_ENABLE
		[GKT_USER_TYPE_FINGERPRINT] = &g_user_fingerprint.instance,
#else
		[GKT_USER_TYPE_FINGERPRINT] = NULL,
#endif
#if (defined(GKT_CONFIG_TOUCH_PANEL_ENABLE) && GKT_CONFIG_TOUCH_PANEL_ENABLE)	\
	|| (defined(GKT_CONFIG_KEYPAD_ENABLE) && GKT_CONFIG_KEYPAD_ENABLE)
	[GKT_USER_TYPE_PASSWORD] = &g_user_password.instance,
#else
	[GKT_USER_TYPE_PASSWORD] = NULL,
#endif
#if defined(GKT_CONFIG_RFID_ENABLE) && GKT_CONFIG_RFID_ENABLE
	[GKT_USER_TYPE_CARD] = &g_user_card.instance,
#else
	[GKT_USER_TYPE_CARD] = NULL,
#endif
#if defined(GKT_CONFIG_FACE_ID_ENABLE) && GKT_CONFIG_FACE_ID_ENABLE
	[GKT_USER_TYPE_FACE_ID] = &g_user_face_id.instance,
#if defined(GKT_CONFIG_PALM_ENABLE) && GKT_CONFIG_PALM_ENABLE
	[GKT_USER_TYPE_PALM] = &g_user_palm.instance,
#endif
#endif
	},

	.items_v = {
		.buffer	= NULL,
		.maxsize	= 0,
		.curr_type	= ~0,
	},
};

__USER_INFO_RETMEM user_info_s g_user_info;


#define USER_TYPE_RETRIEVED_MASK	((1U << GKT_USER_TYPE_MAX) - 1)
static void user_retrieve_all(void)
{
	user_instance_s *instance;
	uint32_t type, mask;

	gkt_trace("user_retrieve_all: type_retrieved_bits(0x%x)\n", 
		g_user_info.type_retrieved_bits);

	for (type = 0; type < GKT_USER_TYPE_MAX; type++) {
		mask = 1U << type;
		if (!(g_user_info.type_retrieved_bits & mask)) {
			instance = g_user.instances[type];
			if (instance && (instance->flags & USER_INSTANCE_F_INIT_OK))
				user_retrieve(instance);
			g_user_info.type_retrieved_bits |= mask;
		}
	}
}

#if (USER_INDEX_CAPACITY > GKT_USER_MAXNUMS)
static void user_set_index_map(uint32_t user_index, uint32_t type)
{
	user_index_type_map_item_u *map_item;

	GKT_ASSERT(user_index >= GKT_USER_INDEX_MIN);
	GKT_ASSERT(user_index <= GKT_USER_INDEX_MAX);

	gkt_trace("set_index_map: user_index(%u), type(%u)\n", user_index, type);
	if (g_user_info.index_type_map.used_nums < GKT_USER_MAXNUMS) {
#if defined(GKT_CONFIG_DEBUG_ENABLE) && GKT_CONFIG_DEBUG_ENABLE
		uint32_t i;
		for (i = 0; i < g_user_info.index_type_map.used_nums; i++) {
			map_item = &g_user_info.index_type_map.map_items_table[i];
			GKT_ASSERT((uint32_t)map_item->user_index != user_index);
		}
#endif

		if (g_user_info.index_type_map.sorted
			&& (g_user_info.index_type_map.used_nums > 0))
		{
			map_item = &g_user_info.index_type_map.map_items_table[g_user_info.index_type_map.used_nums - 1];
			if (user_index < map_item->user_index)
				g_user_info.index_type_map.sorted = 0;
		}

		map_item = &g_user_info.index_type_map.map_items_table[g_user_info.index_type_map.used_nums];
		map_item->user_index = (uint16_t)user_index;
		map_item->type = (uint8_t)type;
		g_user_info.index_type_map.used_nums++;
	}
}
static void user_delete_index_map_item(uint32_t pos)
{
	g_user_info.index_type_map.used_nums--;
	if (pos < g_user_info.index_type_map.used_nums) {
		g_user_info.index_type_map.map_items_table[pos].value =
			g_user_info.index_type_map.map_items_table[g_user_info.index_type_map.used_nums].value;
		g_user_info.index_type_map.sorted = 0;
	}
}
static void user_clear_index_map_index(uint32_t user_index)
{
	user_index_type_map_item_u *map_item;
	uint32_t i;

	gkt_trace("clear_index_map: index(%u)\n", user_index);
	for (i = 0; i < g_user_info.index_type_map.used_nums; i++) {
		map_item = &g_user_info.index_type_map.map_items_table[i];
		if (map_item->user_index == user_index) {
			user_delete_index_map_item(i);
			break;
		}
	}
}
static void user_clear_index_map_type(uint32_t type)
{
	user_index_type_map_item_u *map_item;
	uint32_t i;

	gkt_trace("clear_index_map: type(%u)\n", type);
	for (i = 0; i < g_user_info.index_type_map.used_nums; ) {
		map_item = &g_user_info.index_type_map.map_items_table[i];
		if (map_item->type == type)
			user_delete_index_map_item(i);
		else
			i++;
	}

	g_user_info.type_retrieved_bits &= ~(1U << type);
}

void user_reset_index_map(void)
{
	gkt_trace("reset_index_map: ...\n");
	g_user_info.index_type_map.sorted = 1;
	g_user_info.index_type_map.used_nums = 0;
	memset(g_user_info.index_type_map.map_items_table, 
		0xff, GKT_USER_MAXNUMS * sizeof(user_index_type_map_item_u));
}

uint32_t user_get_type(uint32_t user_index)
{
	uint32_t type, i;

	if (g_user_info.type_retrieved_bits != USER_TYPE_RETRIEVED_MASK)
		user_retrieve_all();

	type = ~0;
	for (i = 0; i < g_user_info.index_type_map.used_nums; i++) {
		if (g_user_info.index_type_map.map_items_table[i].user_index == user_index) {
			type = g_user_info.index_type_map.map_items_table[i].type;
			break;
		}
	}

	return type;
}

uint32_t user_get_free_index(uint32_t authority)
{
	user_index_type_map_item_u *map_item;
	uint32_t user_index, i, pos, pos_min;
	uint16_t map_value;

	gkt_unused(authority);

	if (g_user_info.type_retrieved_bits != USER_TYPE_RETRIEVED_MASK)
		user_retrieve_all();
	
	if (g_user_info.index_type_map.used_nums > 0) {
		if (!g_user_info.index_type_map.sorted) {
			for (pos = 0; pos < g_user_info.index_type_map.used_nums; pos++) {
				map_item = &g_user_info.index_type_map.map_items_table[pos];
				user_index = map_item->user_index;
				pos_min = pos;

				for (i = pos + 1; i < g_user_info.index_type_map.used_nums; i++) {
					map_item = &g_user_info.index_type_map.map_items_table[i];
					if (map_item->user_index < user_index) {
						user_index = map_item->user_index;
						pos_min = i;
					}
				}

				if (pos_min != pos) {
					map_value = g_user_info.index_type_map.map_items_table[pos].value;
					g_user_info.index_type_map.map_items_table[pos].value =
						g_user_info.index_type_map.map_items_table[pos_min].value;
					g_user_info.index_type_map.map_items_table[pos_min].value = map_value;
				}
			}

			g_user_info.index_type_map.sorted = 1;
		}

		user_index = g_user_info.index_type_map.map_items_table[0].user_index;
		if (user_index > GKT_USER_INDEX_MIN)
			user_index = GKT_USER_INDEX_MIN;
		else {
			for (i = 1; i < g_user_info.index_type_map.used_nums; i++) {
				map_item = &g_user_info.index_type_map.map_items_table[i];
				if ((user_index + 1) < map_item->user_index)
					break;
				else
					user_index = map_item->user_index;
			}
			user_index++;
		}
	}
	else
		user_index = GKT_USER_INDEX_MIN;

	return user_index;
}
#else
#define user_index_to_pos(user_index)	(user_index - GKT_USER_INDEX_MIN)
#define pos_to_user_index(position)	(GKT_USER_INDEX_MIN + position)

static void user_set_index_map(uint32_t user_index, uint32_t type)
{
	uint32_t pos;

	gkt_trace("set_index_map: user_index(%u), type(%u)\n", user_index, type);

	if ((user_index >= GKT_USER_INDEX_MIN) 
		&& (user_index <= GKT_USER_INDEX_MAX))
	{
		pos = user_index_to_pos(user_index);
		GKT_ASSERT(g_user_info.index_type_map.index_type_table[pos] >= GKT_USER_TYPE_MAX);
		g_user_info.index_type_map.index_type_table[pos] = type;
	}
}
static void user_clear_index_map_index(uint32_t user_index)
{
	uint32_t pos;

	GKT_ASSERT(user_index >= GKT_USER_INDEX_MIN);
	GKT_ASSERT(user_index <= GKT_USER_INDEX_MAX);
	gkt_trace("clear_index_map: index(%u)\n", user_index);

	pos = user_index_to_pos(user_index);
	g_user_info.index_type_map.index_type_table[pos] = ~0;
}
static void user_clear_index_map_type(uint32_t type)
{
	uint32_t i;

	gkt_trace("clear_index_map: type(%u)\n", type);
	for (i = 0; i < GKT_USER_MAXNUMS; i++) {
		if ((uint32_t)g_user_info.index_type_map.index_type_table[i] == type)
			g_user_info.index_type_map.index_type_table[i] = ~0;
	}
}

void user_reset_index_map(void)
{
	gkt_trace("reset_index_map: ...\n");
	memset(g_user_info.index_type_map.index_type_table, 0xff, GKT_USER_MAXNUMS);
}

uint32_t user_get_type(uint32_t user_index)
{
	uint32_t pos = user_index_to_pos(user_index);

	if (g_user_info.type_retrieved_bits != USER_TYPE_RETRIEVED_MASK)
		user_retrieve_all();

	return g_user_info.index_type_map.index_type_table[pos];
}

uint32_t user_get_free_index(uint32_t authority)
{
	uint32_t i;

	gkt_unused(authority);

	if (g_user_info.type_retrieved_bits != USER_TYPE_RETRIEVED_MASK)
		user_retrieve_all();

	for (i = 0; i < GKT_USER_MAXNUMS; i++) {
		if (g_user_info.index_type_map.index_type_table[i] > GKT_USER_TYPE_MAX)
			return pos_to_user_index(i);
	}

	return ~0;
}
#endif

const user_item_header_u *user_find_item(user_instance_s *instance,
				uint32_t user_index, uint32_t *item_index)
{
	const user_item_header_u *c_item_header;
	uint32_t item_index_tmp;
	int retval;

	retval = user_reload_items_v(instance);
	if (GKT_SUCCESS == retval) {
		item_index_tmp = 0;
		while (item_index_tmp < instance->item_available_nums) {
			c_item_header = USER_ITEM_HEADER_C(instance, item_index_tmp);
			if (USER_ITEM_IS_INDEX_USED(c_item_header, user_index)) {
				if (item_index)
					*item_index = item_index_tmp;
				return c_item_header;
			}
			item_index_tmp++;
		}
	}

	return NULL;
}

static int user_add_item(user_instance_s *instance,
				uint32_t item_index)
{
	const user_item_header_u *c_item_header;
	uint32_t user_index, authority;
	int retval;

	// agan - 20241017
	gkt_trace("user_add_item: type(%u-%u) index(%u)\n",
		g_user.items_v.curr_type, instance->type, item_index);

	c_item_header = USER_ITEM_HEADER_C(instance, item_index);
	authority = c_item_header->authority.type;
	if (instance->info->auth_user_used_nums[authority] 
			< instance->auth_user_maxnums[authority]) 
	{
		user_index = c_item_header->user_index;
		user_set_index_map(user_index, instance->type);
		instance->info->auth_user_used_nums[authority]++;
		instance->info->user_used_nums++;

		if (instance->c_ops->add_item) {
			retval = (*instance->c_ops->add_item)(item_index);
			if (retval != GKT_SUCCESS)
				return retval;
		}

		gkt_trace("user_add_item: (%u) auth(%u), index(%u / %u), nums(%u / %u)\n",
					instance->type, authority, user_index, item_index,
					instance->info->auth_user_used_nums[authority],
					instance->auth_user_maxnums[authority]);

		return GKT_SUCCESS;
	}
	else {
		gkt_fatal("user_add_item: OVERFLOW - used_nums(%u / %u)\n",
				instance->info->auth_user_used_nums[authority],
				instance->auth_user_maxnums[authority]);
		GKT_ASSERT(0);
	}

	return GKT_EOVERFLOW;
}
static void user_delete_item(user_instance_s *instance,
				uint32_t item_index)
{
	const user_item_header_u *c_item_header;
	uint32_t user_index, authority;

	// agan - 20241017
	gkt_trace("user_delete_item: type(%u-%u) index(%u)\n",
		g_user.items_v.curr_type, instance->type, item_index);

	c_item_header = USER_ITEM_HEADER_C(instance, item_index);
	authority = c_item_header->authority.type;
	if ((instance->info->auth_user_used_nums[authority] > 0)
		&& (instance->info->user_used_nums > 0)) 
	{
		if (instance->c_ops->delete_item)
			(*instance->c_ops->delete_item)(item_index);

		instance->info->auth_user_used_nums[authority]--;
		instance->info->user_used_nums--;
		user_index = c_item_header->user_index;
		user_clear_index_map_index(user_index);

		gkt_trace("user_delete_item: (%u) auth(%u), index(%u / %u), nums(%u / %u)\n",
					instance->type, authority, user_index, item_index,
					instance->info->auth_user_used_nums[authority],
					instance->auth_user_maxnums[authority]);
	}
	else {
		gkt_error("user_delete_item: (%u)(%u) NO USED AUTH USER\n", instance->type, authority);
	}

	user_update_item_state(instance, item_index, USER_ITEM_ST_DELETED);
}

int user_item_is_used(user_instance_s *instance, 
			uint32_t item_index)
{
	const user_item_header_u *c_item_header;

	c_item_header = USER_ITEM_HEADER_C(instance, item_index);
	return USER_ITEM_IS_USED(c_item_header) ? 1 : 0;
}

void user_update_item_state(user_instance_s *instance, 
				uint32_t item_index, uint8_t state)
{
	user_item_header_u *v_item_header;
	uint32_t offset;
	int retval;

	gkt_trace("user_update_item_state: (%u) item_index(%u) state(0x%x)\n",
			instance->type, item_index, state);

	user_reload_items_v(instance);	// agan - 241017
	if (!instance->c_flash_info->read_map_addr
		&& instance->v_items_base) 
	{
		v_item_header = USER_ITEM_HEADER_V(instance, item_index);
		v_item_header->state = state;
	}

	offset = USER_ITEM_OFFSET(instance, item_index) 
				+ __MEMBER_OFFSET(user_item_header_u, state);
	retval = gkt_flash_partition_write(instance->c_flash_info->id, 
					offset, sizeof(state), &state, 0);
	if (retval != GKT_SUCCESS) {
		gkt_error("user_update_item_state: write flash failed %d\n", retval);
	}

	gkt_unused(retval);
}

void user_reset_items_v(user_instance_s *instance)
{
	if (!instance->c_flash_info->read_map_addr
		&& (g_user.items_v.curr_type == instance->type))
	{
		g_user.items_v.curr_type = ~0;
	}
}

int __user_reload_items_v(user_instance_s *instance, const char *func, int line)
{
	uint32_t item_total_size;
	int retval = GKT_SUCCESS;

	// agan - 20241017
	gkt_trace("user_reload_items_v: type(%u-%u) v_items_base(%p)\n",
		g_user.items_v.curr_type, instance->type, instance->v_items_base);
	gkt_trace("user_reload_items_v: from %s at line %d\n", func, line);

	if (!instance->c_flash_info->read_map_addr
		&& ((g_user.items_v.curr_type != instance->type)
			|| !instance->v_items_base)) 
	{
		g_user.items_v.curr_type = ~0;

		item_total_size = instance->item_size * instance->item_available_nums;
		if (g_user.items_v.maxsize < item_total_size) {
			if (g_user.items_v.buffer) {
				gkt_free(g_user.items_v.buffer);
				g_user.items_v.buffer = NULL;
			}
			g_user.items_v.maxsize = gkt_aligned(item_total_size, 1024);
		}

		if (!g_user.items_v.buffer) {
			gkt_trace("user_reload_items_v: maxsize = 0x%x\n", g_user.items_v.maxsize);
			g_user.items_v.buffer = (uint8_t *)gkt_malloc(g_user.items_v.maxsize);
			if (!g_user.items_v.buffer) {
				gkt_error("user_reload_items_v: alloc buffer failed!\n");
				return GKT_ENOMEMORY;
			}
		}
		memset(g_user.items_v.buffer, 0xff, g_user.items_v.maxsize);

		retval = gkt_flash_partition_read(instance->c_flash_info->id,
					instance->item_base_offset, item_total_size, 
					g_user.items_v.buffer);
		if (GKT_SUCCESS == retval) {
			instance->v_items_base = (user_item_header_u *)g_user.items_v.buffer;
			g_user.items_v.curr_type = instance->type;
		}
		else
			gkt_error("user_reload_items_v: read failed!\n");
	}

	return retval;
}

int user_save_item(user_instance_s *instance,
				uint32_t item_index, uint8_t state)
{
	user_item_header_u *v_item_header = NULL;
	uint32_t offset;
	int retval, pre_erase;

	// agan - 20241017
	gkt_trace("user_save_item: type(%u-%u) index(%u) state(0x%x)\n",
		g_user.items_v.curr_type, instance->type, item_index, state);

	gkt_trace("user_save_item: (%u) index(%u) state(0x%x)\n", 
			instance->type, item_index, state);

	if (instance->item_data_maxsize > 0) {
		/* step-1: save split data */
		if (!instance->item_data
			|| !instance->item_data_size
			|| (instance->item_data_size > instance->item_data_maxsize))
		{
			gkt_error("user_save_item: <D> split data or size invalid!\n");
			return GKT_ENOMATCHED;
		}

		offset = USER_ITEM_DATA_OFFSET(instance, item_index);
		pre_erase = (instance->flags & USER_INSTANCE_F_ITEM_DATA_SECTOR_ALIGNED) ? 1 : 0;
		retval = gkt_flash_partition_write(instance->c_flash_info->id,
						offset, instance->item_data_size,
						instance->item_data, pre_erase);
		if (retval != GKT_SUCCESS) {
			gkt_error("user_save_item: <D> save data failed %d\n", retval);
			return retval;
		}
	}

	// agan - 20241017
	gkt_trace("user_save_item: type(%u-%u) v_items_base(%p)\n", 
		g_user.items_v.curr_type, instance->type,
		instance->v_items_base);

	/* step-2: save item */
	user_reload_items_v(instance);	// agan - 241017
	instance->item_header->state = USER_ITEM_ST_SAVING;
	if (!instance->c_flash_info->read_map_addr 
		&& instance->v_items_base) 
	{
		v_item_header = USER_ITEM_HEADER_V(instance, item_index);
		memcpy(v_item_header, instance->item_header, instance->item_size);
	}

	offset = USER_ITEM_OFFSET(instance, item_index);
	retval = gkt_flash_partition_write(instance->c_flash_info->id, 
					offset, instance->item_size, 
					instance->item_header, 0);
	if (retval != GKT_SUCCESS) {
		gkt_error("user_save_item: save failed %d\n", retval);
		return retval;
	}

	/* step-3: update item state */
	instance->item_header->state = state;
	offset += __MEMBER_OFFSET(user_item_header_u, state);
	retval = gkt_flash_partition_write(instance->c_flash_info->id, 
					offset, sizeof(instance->item_header->state), 
					&instance->item_header->state, 0);
	if (GKT_SUCCESS == retval) {
		if (v_item_header)
			v_item_header->state = state;
	}
	else {
		gkt_error("user_save_item: update state failed %d\n", retval);
		user_update_item_state(instance, item_index, USER_ITEM_ST_DELETED);
	}

	return retval;
}

void user_delete_index(user_instance_s *instance,
				uint32_t user_index)
{
	const user_item_header_u *c_item_header;
	uint32_t item_index;
	int retval;

	gkt_trace("user_delete_index: (%u) user_index(%u)\n",
				instance->type, user_index);

	retval = user_reload_items_v(instance);
	if (GKT_SUCCESS == retval) {
		item_index = 0;
		while (item_index < instance->item_available_nums) {
			c_item_header = USER_ITEM_HEADER_C(instance, item_index);
			if (USER_ITEM_IS_INDEX_USED(c_item_header, user_index)) {
				user_delete_item(instance, item_index);
				break;
			}

			item_index++;
		}
	}
}

void user_delete_authority(user_instance_s *instance, 
				uint32_t authority)
{
	const user_item_header_u *c_item_header;
	uint32_t item_index;
	int retval;

	gkt_trace("user_delete_authority: (%u) auth(%u), used_nums(%u)\n",
				instance->type, authority, 
				instance->info->auth_user_used_nums[authority]);

	retval = user_reload_items_v(instance);
	if (GKT_SUCCESS == retval) {
		item_index = 0;
		while (item_index < instance->item_available_nums) {
			c_item_header = USER_ITEM_HEADER_C(instance, item_index);
			if (USER_ITEM_IS_AUTH_USED(c_item_header, authority))
				user_delete_item(instance, item_index);
			item_index++;
		}
	}
}

static void user_reset_info(user_instance_s *instance)
{
	uint32_t i;

	gkt_trace("user_reset_info: type(%u)\n", instance->type);

	if ((instance->type < GKT_USER_TYPE_MAX) 
		&& instance->info) 
	{
		g_user_info.type_retrieved_bits &= ~(1U << instance->type);
		user_clear_index_map_type(instance->type);
		if (instance->c_ops->reset_info)
			(*instance->c_ops->reset_info)();

		instance->info->magic = 0;
		instance->info->item_free_index = ~0;
		for (i = 0; i < GKT_USER_AUTH_TEMPORARY; i++)
			instance->info->auth_user_used_nums[i] = 0;
		instance->info->user_used_nums = 0;
		instance->info->magic = USER_TYPE_INFO_MAGIC;

		instance->flags |= USER_INSTANCE_F_NEED_RETRIEVE;
	}
}

void user_reset_common_info(void)
{
	user_reset_index_map();
	g_user_info.type_retrieved_bits = 0;
	g_user_info.magic = USER_INFO_MAGIC;
}

void user_init_common_info(void)
{
	if (g_user_info.magic != USER_INFO_MAGIC)
		user_reset_common_info();
}

void user_reset_partition(user_instance_s *instance)
{
	user_partition_header_s partition_header;
	int retval, try_times;

	gkt_trace("user_reset_partition: type(%u)\n", instance->type);

	if (instance->c_ops 
		&& instance->c_ops->reset_partition)
	{
		(*instance->c_ops->reset_partition)();
	}

	if (instance->c_flash_info) {
		/* init flash partition header fields */
		memset(&partition_header, 0xff, USER_PARTITION_HEADER_SIZE);
		partition_header.magic = USER_MAGIC;
		partition_header.type = instance->type;
		partition_header.state = USER_PARTITION_ST_NORMAL;

		/* reset flash */
		try_times = USER_FLASH_TRYTIMES;
		do {
			retval = gkt_flash_partition_erase_all(instance->c_flash_info->id);
			if (retval != GKT_SUCCESS) {
				gkt_error("user_reset_partition: erase failed %d\n", retval);
				continue;
			}

			/* init partition header */
			retval = gkt_flash_partition_write(instance->c_flash_info->id,
							0, USER_PARTITION_HEADER_SIZE,
							&partition_header, 0);
			if (retval != GKT_SUCCESS) {
				gkt_error("user_reset_partition: saving failed %d\n", retval);
				continue;
			}

			break;
		} while (--try_times > 0);
	}
}

void user_retrieve(user_instance_s *instance)
{
	const user_item_header_u *c_item_header_curr;
	uint32_t item_index_curr, item_index_exist;
	int retval;

	gkt_trace("user_retrieve: type(%u)\n", instance->type);

	user_reset_info(instance);
	
	retval = user_reload_items_v(instance);
	if (retval != GKT_SUCCESS)
		return;

	item_index_curr = 0;
	if (!(instance->flags & USER_INSTANCE_F_ITEM_HEADER_EXTEND)) {
		while (item_index_curr < instance->item_available_nums) {
			c_item_header_curr = USER_ITEM_HEADER_C(instance, item_index_curr);
			if (USER_ITEM_IS_USED(c_item_header_curr))
				user_add_item(instance, item_index_curr);
			else if (USER_ITEM_IS_FREE(c_item_header_curr)) {
				gkt_trace("user_retrieve: <FREE> (%02u)\n", item_index_curr);
				if (item_index_curr < instance->info->item_free_index)
					instance->info->item_free_index = item_index_curr;
			}
			else {
				gkt_trace("user_retrieve: <DEL> (%02u)\n", item_index_curr);
			}

			item_index_curr++;
		}
	}
	else {
		const user_item_extend_header_u *c_item_ext_header_curr;
		const user_item_extend_header_u *c_item_ext_header_exist;

		while (item_index_curr < instance->item_available_nums) {
			c_item_ext_header_curr = USER_ITEM_EXTEND_HEADER_C(instance, item_index_curr);
			if (USER_ITEM_IS_USED(c_item_ext_header_curr)) {
				gkt_trace("user_retrieve: <USED> (%02u) user_index(%u), auth(%u), seq(%u)\n", 
						item_index_curr, c_item_ext_header_curr->user_index, 
						c_item_ext_header_curr->authority.type, 
						c_item_ext_header_curr->seq);

				item_index_exist = 0;
				while (item_index_exist < item_index_curr) {
					c_item_ext_header_exist = USER_ITEM_EXTEND_HEADER_C(instance, item_index_exist);
					if (USER_ITEM_IS_INDEX_USED(c_item_ext_header_exist, c_item_ext_header_curr->user_index)) {
						gkt_trace("user_retrieve: <EXIST> (%02u) seq(%u)\n",
								item_index_exist, c_item_ext_header_exist->seq);

						if (c_item_ext_header_curr->seq <= c_item_ext_header_exist->seq) {
							/* delete current out-of-date item */
							user_update_item_state(instance, item_index_curr, USER_ITEM_ST_DELETED);
						}
						else {
							/* delete exist out-of-date item */
							user_delete_item(instance, item_index_exist);

							/* add current item */
							user_add_item(instance, item_index_curr);
						}

						break;
					}

					item_index_exist++;
				}

				if (item_index_exist == item_index_curr)
					user_add_item(instance, item_index_curr);
			}
			else if (USER_ITEM_IS_FREE(c_item_ext_header_curr)) {
				gkt_trace("user_retrieve: <FREE> (%02u)\n", item_index_curr);
				if (instance->info->item_free_index > item_index_curr)
					instance->info->item_free_index = item_index_curr;
			}
			else {
				gkt_trace("user_retrieve: <DEL> (%02u)\n", item_index_curr);
			}

			item_index_curr++;
		}
	}

	g_user_info.type_retrieved_bits |= 1U << instance->type;
	instance->flags &= ~USER_INSTANCE_F_NEED_RETRIEVE;

	gkt_trace("user_retrieve: free_index(%u), used_nums(%u-%u-%u)\n", 
			instance->info->item_free_index,
			instance->info->auth_user_used_nums[GKT_USER_AUTH_ADMIN],
			instance->info->auth_user_used_nums[GKT_USER_AUTH_NORMAL],
			instance->info->auth_user_used_nums[GKT_USER_AUTH_GUEST]);
}

/* recycle deleted items */
static int user_recycle_save(user_instance_s *instance,
				uint32_t offset, uint8_t *buffer)
{
	user_partition_header_s *partition_header;
	int retval, try_times;

	GKT_ASSERT(!(offset & (instance->c_flash_info->sector_size - 1)));

	try_times = USER_FLASH_TRYTIMES;
	do {
		/* 1st. save to stash partition */
		retval = user_stash_save(instance->type, offset, 
						instance->c_flash_info->sector_size, buffer);
		if (retval != GKT_SUCCESS) {
			gkt_error("user_recycle: save stash failed %d.\n", retval);
			continue;
		}

		if (!offset) {
			partition_header = (user_partition_header_s *)buffer;
			partition_header->state = USER_PARTITION_ST_SAVING;
		}

		/* 2nd. save to current partition */
		retval = gkt_flash_partition_write(instance->c_flash_info->id,
						offset, instance->c_flash_info->sector_size, 
						buffer, 1);
		if (retval != GKT_SUCCESS) {
			gkt_error("user_recycle_save: write flash failed %d.\n", retval);
			continue;
		}

		if (!offset) {
			partition_header->state = USER_PARTITION_ST_RECYCLE_STARTED;
			retval = gkt_flash_partition_write(instance->c_flash_info->id,
						__MEMBER_OFFSET(user_partition_header_s, state),
						sizeof(partition_header->state), &partition_header->state, 0);
			if (retval != GKT_SUCCESS) {
				gkt_error("user_recycle_save: update recycle state failed %d.\n", retval);
				continue;
			}
		}

		break;
	} while (--try_times > 0);

	return retval;
}

static int user_recycle_items(user_instance_s *instance,
				uint32_t start, uint8_t *buffer)
{
	const user_item_header_u *c_item_header;
	uint32_t offset, end, aligned_offset; 
	uint32_t item_index, item_offset_end;
	uint32_t data_offset, data_size, tmp_size;
	int update_flash, retval;

	gkt_trace("user_recycle: <I> start(0x%x)\n", start);

	aligned_offset = start & (~(instance->c_flash_info->sector_size - 1));
	if (aligned_offset > instance->item_base_offset) {
		item_index = (aligned_offset - instance->item_base_offset) / instance->item_size;
		if (item_index >= instance->item_available_nums)
			return GKT_SUCCESS;
	}
	else
		item_index = 0;

	offset = aligned_offset;
	end = instance->item_base_offset + (instance->item_size * instance->item_available_nums);
	data_offset = 0;
	while (offset < end) {
		if (!data_offset) {
			data_size = end - aligned_offset;
			if (data_size > instance->c_flash_info->sector_size)
				data_size = instance->c_flash_info->sector_size;

			/* read flash data to recycle buffer */
			retval = gkt_flash_partition_read(instance->c_flash_info->id,
						aligned_offset, instance->c_flash_info->sector_size, 
						buffer);
			if (retval != GKT_SUCCESS) {
				gkt_error("user_recycle: <I> read flash failed %d\n", retval);
				return retval;
			}

			if (!aligned_offset) {
				offset = instance->item_base_offset;
				data_offset = instance->item_base_offset;
				update_flash = 1;
			}
			else
				update_flash = 0;
		}

		item_offset_end = USER_ITEM_OFFSET(instance, item_index) + instance->item_size;
		tmp_size = item_offset_end - offset;
		if ((data_offset + tmp_size) > data_size)
			tmp_size = data_size - data_offset;

		c_item_header = USER_ITEM_HEADER_C(instance, item_index);
		if (USER_ITEM_IS_UNUSED(c_item_header)) {
			memset(&buffer[data_offset], 0xff, tmp_size);
			update_flash = 1;
		}

		offset += tmp_size;
		data_offset += tmp_size;

		/* save recycled sector data */
		if (data_offset == data_size) {
			if (update_flash) {
				user_recycle_save(instance, aligned_offset, buffer);
				if (retval != GKT_SUCCESS) {
					gkt_error("user_recycle: <I> save failed %d\n", retval);
					return retval;
				}
			}

			data_offset = 0;
			aligned_offset += instance->c_flash_info->sector_size;
		}

		if (offset == item_offset_end)
			item_index++;
	}

	return GKT_SUCCESS;
}

static int user_recycle_items_split_data(user_instance_s *instance,
				uint32_t start, uint8_t *buffer)
{
	uint32_t offset, end, aligned_offset; 
	uint32_t item_index, item_offset_end;
	uint32_t data_offset, data_size, tmp_size;
	int item_used, update_flash, retval;

	gkt_trace("user_recycle: <ISD> start(0x%x)\n", start);
	
	aligned_offset = start & (~(instance->c_flash_info->sector_size - 1));
	GKT_ASSERT(aligned_offset >= instance->item_data_base_offset);

	if (aligned_offset > instance->item_data_base_offset) {
		item_index = (aligned_offset - instance->item_data_base_offset) / instance->item_data_maxsize;
		if (item_index >= instance->item_available_nums)
			return GKT_SUCCESS;
	}
	else
		item_index = 0;
	item_used = user_item_is_used(instance, item_index);
	item_offset_end = USER_ITEM_DATA_OFFSET(instance, item_index) + instance->item_data_maxsize;

	data_offset = 0;
	offset = aligned_offset;
	end = instance->item_data_base_offset 
			+ (instance->item_data_maxsize * instance->item_available_nums);
	while (offset < end) {
		if (!data_offset) {
			if (!item_used 
				&& ((offset + instance->c_flash_info->sector_size) <= item_offset_end)) 
			{
				retval = gkt_flash_partition_erase(instance->c_flash_info->id,
								offset, instance->c_flash_info->sector_size);
				if (retval != GKT_SUCCESS) {
					gkt_error("user_recycle: <ISD> erase flash failed %d\n", retval);
					return retval;
				}

				offset += instance->c_flash_info->sector_size;
				aligned_offset += instance->c_flash_info->sector_size;

				goto next_item;
			}

			/* read flash data to recycle buffer */
			retval = gkt_flash_partition_read(instance->c_flash_info->id,
							aligned_offset, instance->c_flash_info->sector_size, 
							buffer);
			if (retval != GKT_SUCCESS) {
				gkt_error("user_recycle: <ISD> read flash failed %d\n", retval);
				return retval;
			}

			update_flash = 0;
			data_size = end - offset;
			if (data_size > instance->c_flash_info->sector_size)
				data_size = instance->c_flash_info->sector_size;
		}

		tmp_size = item_offset_end - offset;
		if ((data_offset + tmp_size) > data_size)
			tmp_size = data_size - data_offset;
		if (!item_used) {
			memset(&buffer[data_offset], 0xff, tmp_size);
			update_flash = 1;
		}
		offset += tmp_size;
		data_offset += tmp_size;

		/* save recycled sector data */
		if (data_offset == data_size) {
			if (update_flash) {
				user_recycle_save(instance, aligned_offset, buffer);
				if (retval != GKT_SUCCESS) {
					gkt_error("user_recycle: <ISD> save failed %d\n", retval);
					return retval;
				}
			}

			data_offset = 0;
			aligned_offset += instance->c_flash_info->sector_size;
		}

next_item:
		if (offset == item_offset_end) {
			item_index++;
			item_used = user_item_is_used(instance, item_index);
			item_offset_end += instance->item_data_maxsize;
		}
	}

	return GKT_SUCCESS;
}

static void user_recycle_complete(user_instance_s *instance)
{
	user_partition_header_s partition_header;
	int retval;

	partition_header.state = USER_PARTITION_ST_NORMAL;
	retval = gkt_flash_partition_write(instance->c_flash_info->id,
					__MEMBER_OFFSET(user_partition_header_s, state), 
					sizeof(partition_header.state), 
					&partition_header.state, 0);
	if (retval != GKT_SUCCESS)
		gkt_error("useer_recycle: update complete failed %d\n", retval);

	user_stash_reset();
	user_reset_items_v(instance);
	user_reload_items_v(instance);
}

int user_recycle(user_instance_s *instance, int stash_restore)
{
	uint8_t *buffer;
	uint32_t offset;
	int retval;

	gkt_info("user_recycle: (%u) stash_restore(%d)\n", 
			instance->type, stash_restore);

	retval = user_reload_items_v(instance);
	if (retval != GKT_SUCCESS) {
		gkt_error("user_recycle: reload_items_v failed!\n");
		return retval;
	}

	buffer = (uint8_t *)gkt_malloc(instance->c_flash_info->sector_size);
	if (!buffer) {
		gkt_error("user_recycle: alloc buffer failed!\n");
		return GKT_ENOMEMORY;
	}

	instance->flags |= USER_INSTANCE_F_NEED_RETRIEVE;

	if (stash_restore) {
		retval = user_stash_apply(instance->c_flash_info, buffer);
		if (retval != GKT_SUCCESS)
			goto done;

		offset = g_user_stash.c_partition_header->info.offset
					+ instance->c_flash_info->sector_size;
	}
	else {
		if ((instance->item_data_base_offset > 0)
			&& !(instance->flags & USER_INSTANCE_F_ITEM_DATA_SECTOR_ALIGNED))
		{
			offset = instance->item_data_base_offset;
		}
		else
			offset = 0;
	}

	/* recycle items split data */
	if ((instance->item_data_base_offset > 0)
		&& (offset >= instance->item_data_base_offset)) 
	{
		retval = user_recycle_items_split_data(instance, offset, buffer);
		if (retval != GKT_SUCCESS) {
			gkt_error("user_recycle: items_split_data failed.\n");
			goto done;
		}
	
		/* recycle items split data completely, then start recycling items */
		offset = 0;
	}

	/* recycle items */
	retval = user_recycle_items(instance, offset, buffer);
	if (retval != GKT_SUCCESS) {
		gkt_error("user_recycle: items failed.\n");
		goto done;
	}

	/* recycle completely */
	user_recycle_complete(instance);

done:
	gkt_free(buffer);
	return retval;
}

static uint32_t user_get_free_item(user_instance_s *instance)
{
	const user_item_header_u *c_item_header;
	uint32_t start_item_index, end_item_index, item_index;
	uint32_t unused_nums;
	int recycled, retval;

	GKT_ASSERT(instance->type < GKT_USER_TYPE_MAX);
	GKT_ASSERT(instance->info);

	retval = user_reload_items_v(instance);
	if (retval != GKT_SUCCESS)
		return retval;

	if (instance->info->item_free_index >= instance->item_available_nums)
		instance->info->item_free_index = 0;

	recycled = 0;
check_start:
	unused_nums = 0;
	start_item_index = instance->info->item_free_index;
	end_item_index = instance->info->item_free_index + instance->item_available_nums;
	while (start_item_index < end_item_index) {
		item_index = (start_item_index < instance->item_available_nums) ?
						start_item_index : (start_item_index - instance->item_available_nums);
		c_item_header = USER_ITEM_HEADER_C(instance, item_index);
		if (USER_ITEM_IS_FREE(c_item_header)) {
			instance->info->item_free_index = item_index;
			gkt_trace("user_get_free_item: index(%u)\n", item_index);
			return instance->info->item_free_index;
		}
		else if (!USER_ITEM_IS_USED(c_item_header))
			unused_nums++;

		start_item_index++;
	}

	gkt_trace("user_get_free_item: (%d) unused_nums(%u)\n", 
			recycled, unused_nums);
	if (!recycled && (unused_nums > 0)) {
		recycled = 1;
		retval = user_recycle(instance, 0);
		if (GKT_SUCCESS == retval) {
			instance->info->item_free_index = 0;
			goto check_start;
		}
	}

	gkt_info("user_get_free_item: FAILED - no free item\n");

	return ~0;
}

int user_enroll_init(user_instance_s *instance,
				uint32_t authority, uint32_t threaten, 
				uint32_t user_index)
{
	uint32_t enroll_item_index;
	int retval;

	enroll_item_index = user_get_free_item(instance);
	if (enroll_item_index < instance->item_available_nums) {
		if (instance->c_ops->enroll_init) {
			retval = (*instance->c_ops->enroll_init)();
			if (retval != GKT_SUCCESS) {
				gkt_error("user_enroll_init: (%u) type enroll_init failed %d\n",
							instance->type, retval);
				return retval;
			}
		}

		instance->enroll_item_index = enroll_item_index;
		instance->item_header->user_index = user_index;
		instance->item_header->authority.type = authority;
		instance->item_header->authority.threaten = threaten;
		instance->item_data = NULL;
		instance->item_data_size = 0;

		retval = GKT_SUCCESS;
	}
	else
		retval = GKT_ENORESOURCE;

	return retval;
}
int user_enroll_run(user_instance_s *instance,
				const void *data, uint32_t length)
{
	int retval;

	if (instance->c_ops->enroll_run) {
		retval = (*instance->c_ops->enroll_run)(data, length);
		if (GKT_USER_ENROLL_COMPLETE == retval) {
			if (instance->flags & USER_INSTANCE_F_ITEM_HEADER_EXTEND) {
				if (instance->flags & USER_INSTANCE_F_ITEM_UPDATE_ENABLE) {
					instance->item_extend_header->update_state = USER_ITEM_UPDATE_ENABLE;
					instance->item_extend_header->seq = USER_ITEM_UPDATE_SEQ_INITIAL;
				}
				else {
					instance->item_extend_header->update_state = USER_ITEM_UPDATE_DISABLE;
					instance->item_extend_header->seq = ~0;
				}
				instance->item_extend_header->reserved = ~0;
			}
			
			retval = user_save_item(instance, instance->enroll_item_index, USER_ITEM_ST_USED);
			if (retval != GKT_SUCCESS) {
				gkt_error("user_enroll_run: save user(%u) item(%u) failed %d\n",
					instance->item_header->user_index,instance->enroll_item_index, 
					retval);
				return retval;
			}

			retval = user_add_item(instance, instance->enroll_item_index);
		}
	}
	else {
		retval = GKT_ENOTSUPPORT;
		gkt_fatal("user_enroll_run: c_ops->enroll_run NULL!\n");
	}

	return retval;
}
void user_enroll_uninit(user_instance_s *instance)
{
	gkt_trace("user_enroll_uninit: %u\n", instance->enroll_item_index);

	if (instance->c_ops->enroll_uninit)
		(*instance->c_ops->enroll_uninit)();
	instance->enroll_item_index = ~0;
}

int user_verify(user_instance_s *instance, uint32_t *user_id, 
				uint32_t excluded_user_index, uint32_t authority,
				const void *data, uint32_t length)
{
	const user_item_extend_header_u *c_item_extend_header;
	int retval;

	GKT_ASSERT(instance->info);
	if (instance->info) {
		if (!instance->info->user_used_nums) {
			gkt_trace("user_verify: (%u) no used item!\n", instance->type);
			return GKT_USER_VERIFY_FAILED;
		}
		else if ((authority < GKT_USER_AUTH_TEMPORARY)
			&& !instance->info->auth_user_used_nums[authority])
		{
			gkt_trace("user_verify: (%u) no used auth(%u) item!\n", 
				instance->type, authority);
			return GKT_USER_VERIFY_FAILED;
		}
	}

	if (instance->c_ops->verify) {
		retval = user_reload_items_v(instance);
		if (retval != GKT_SUCCESS)
			return retval;

		*user_id = 0;
		instance->verify_item_index = ~0;
		retval = (*instance->c_ops->verify)(user_id, excluded_user_index, 
						authority, data, length);
		gkt_trace("user_verify: <%d> user_id = 0x%x\n", retval, *user_id);

		if ((GKT_USER_VERIFY_PASSED == retval)
			&& (instance->flags & USER_INSTANCE_F_ITEM_UPDATE_ENABLE)) 
		{
			c_item_extend_header = (const user_item_extend_header_u *)user_find_item(instance, 
				GKT_USER_INDEX(*user_id), &instance->verify_item_index);
			if (!c_item_extend_header) {
				gkt_fatal("user_verify: (%u) no matched item for id(0x%x)\n",
					instance->type, *user_id);
				retval = GKT_USER_VERIFY_FAILED;
			}
			else if (USER_ITEM_UPDATE_ENABLE == c_item_extend_header->update_state) {
				gkt_trace("user_verify: NEED UPDATE ...\n");
				retval = GKT_USER_VERIFY_PASSED_UPDATE;
			}
		}
	}
	else {
		gkt_fatal("user_verify: c_ops->verify NULL!\n");
		GKT_ASSERT(0);
		retval = GKT_EFATAL;
	}

	return retval;
}

static void user_verify_update_disable(user_instance_s *instance,
				uint32_t item_index)
{
	user_item_extend_header_u *item_extend_header;
	uint8_t update_state;
	uint32_t offset;
	int retval;

	if (!instance->c_flash_info->read_map_addr) {
		item_extend_header = USER_ITEM_EXTEND_HEADER_V(instance, item_index);
		item_extend_header->update_state = USER_ITEM_UPDATE_DISABLE;
	}

	update_state = USER_ITEM_UPDATE_DISABLE;
	offset = USER_ITEM_OFFSET(instance, item_index)
			+ __MEMBER_OFFSET(user_item_extend_header_u, update_state);
	retval = gkt_flash_partition_write(instance->c_flash_info->id,
					offset, sizeof(update_state), &update_state, 0);
	if (retval != GKT_SUCCESS)
		gkt_error("user_verify_update_disable: modify update_state failed!\n");
}

void user_verify_update(user_instance_s *instance)
{
	const user_item_extend_header_u *c_item_extend_header;
	uint32_t item_index;
	int retval;

	gkt_trace("user_verify_update: old item_index = %u\n",
				instance->verify_item_index);

	c_item_extend_header = USER_ITEM_EXTEND_HEADER_C(instance, 
							instance->verify_item_index);
	if (USER_ITEM_UPDATE_ENABLE == c_item_extend_header->update_state) {
		item_index = user_get_free_item(instance);
		if (item_index < instance->item_available_nums) {
			retval = (*instance->c_ops->verify_update)();
			gkt_trace("user_verify_update: result = %u\n", retval);

			if (USER_VERIFY_UPDATE_SUCCESS == retval) {
				instance->item_extend_header->attr = c_item_extend_header->attr;
				instance->item_extend_header->update_state = USER_ITEM_UPDATE_ENABLE;
				instance->item_extend_header->seq = c_item_extend_header->seq + 1;
				instance->item_extend_header->reserved = ~0;

				/* save updated item */
				retval = user_save_item(instance, item_index, USER_ITEM_ST_USED);
				if (GKT_SUCCESS == retval) {
					user_delete_item(instance, instance->verify_item_index);	/* delete old item */
					user_add_item(instance, item_index);	/* add updated item */
				}
			}
			else if (USER_VERIFY_UPDATE_DISABLE == retval)
				user_verify_update_disable(instance, instance->verify_item_index);
		}
		else
			gkt_warning("user_verify_update: no available item!\n");
	}
}

void user_verify_finalize(user_instance_s *instance)
{
	if (instance->c_ops->verify_finalize)
		(*instance->c_ops->verify_finalize)();
	instance->verify_item_index = ~0;
}

void user_reset(user_instance_s *instance)
{
	user_reset_info(instance);
	user_reset_items_v(instance);
	user_reset_partition(instance);
}

static int user_init_partition(user_instance_s *instance)
{
	uint32_t size, item_nums, item_data_nums;
	uint32_t alignmask;
	int retval;

	gkt_trace("user_init_partition: name = %s\n", 
			instance->c_partition_name);

	instance->c_flash_info = 
		gkt_flash_partition_get_info(instance->c_partition_name);
	if (!instance->c_flash_info) {
		gkt_fatal("user_init_partition: get partition(%s) info failed!\n", 
			instance->c_partition_name);
		return GKT_ENOTEXIST;
	}
	gkt_trace("user_init_partition: flash size(%u - %u), map_addr(%p)\n",
			instance->c_flash_info->size, 
			instance->c_flash_info->sector_size,
			instance->c_flash_info->read_map_addr);

	instance->flags &= ~(USER_INSTANCE_F_ITEM_SECTOR_ALIGNED
						| USER_INSTANCE_F_ITEM_DATA_SECTOR_ALIGNED);
	instance->item_size = (instance->item_size + USER_ALIGNMASK) & (~USER_ALIGNMASK);
	instance->item_base_offset = USER_PARTITION_HEADER_SIZE;

	if (!instance->item_data_maxsize) {
		instance->item_data_base_offset = 0;
		instance->item_available_nums = (instance->c_flash_info->size - instance->item_base_offset) / instance->item_size;
	}
	else {
		alignmask = instance->c_flash_info->sector_size - 1;
		size = instance->item_base_offset + (instance->user_maxnums * instance->item_size);
		instance->item_data_base_offset = (size + alignmask) & (~alignmask);
		GKT_ASSERT(instance->item_data_base_offset < instance->c_flash_info->size);
		item_nums = (instance->item_data_base_offset - instance->item_base_offset) / instance->item_size;

		instance->item_data_maxsize = (instance->item_data_maxsize + USER_ALIGNMASK) & (~USER_ALIGNMASK);
		if (!(instance->item_data_maxsize % instance->c_flash_info->sector_size))
			instance->flags |= USER_INSTANCE_F_ITEM_DATA_SECTOR_ALIGNED;
		size = (instance->c_flash_info->size - instance->item_data_base_offset);
		item_data_nums = size / instance->item_data_maxsize;

		gkt_trace("user_init_partition: item_nums(%u @ %u)\n", item_nums, item_data_nums);
		instance->item_available_nums = gkt_min(item_nums, item_data_nums);
	}

	gkt_trace("user_init_partition: available_nums(%u / %u), base_offset(0x%x - 0x%x)\n",
			instance->user_maxnums, instance->item_available_nums, 
			instance->item_base_offset, instance->item_data_base_offset);
	if (instance->item_available_nums < instance->user_maxnums) {
		gkt_error("user_init_partition: partition(%s) space insufficient!\n", 
			instance->c_partition_name);
		return GKT_EINVALIDCONFIG;
	}

	if (instance->c_flash_info->read_map_addr) {
		instance->c_partition_header = (const user_partition_header_s *)
			instance->c_flash_info->read_map_addr;
		instance->c_items_base = (const user_item_header_u *)
			((uint32_t)instance->c_partition_header + instance->item_base_offset);
	}
	else {
		retval = gkt_flash_partition_read(instance->c_flash_info->id,
						0, USER_PARTITION_HEADER_SIZE, 
						instance->v_partition_header);
		if (retval != GKT_SUCCESS) {
			gkt_error("user_init_partition: read partition(%s) header failed!\n", 
				instance->c_partition_name);
			return retval;
		}
	}

	return GKT_SUCCESS;
}

int user_init(user_instance_s *instance)
{
	user_partition_header_s v_partition_header;
	uint32_t authority;
	int retval;

	GKT_ASSERT(instance->c_partition_name);
	GKT_ASSERT(instance->item_size > USER_ITEM_HEADER_SIZE);
	GKT_ASSERT(instance->item_header);

	gkt_trace("user_init: type(%u), flags(0x%x)\n", 
			instance->type, instance->flags);

	/* initial value */
	instance->flags &= ~(USER_INSTANCE_F_INIT_OK | USER_INSTANCE_F_NEED_RETRIEVE);
	instance->v_partition_header = &v_partition_header;
	instance->c_items_base = NULL;
	instance->user_maxnums = 0;
	for (authority = 0; authority < GKT_USER_AUTH_TEMPORARY; authority++)
		instance->user_maxnums += instance->auth_user_maxnums[authority];
	instance->item_base_offset = 0;
	instance->item_data_base_offset = 0;
	instance->item_available_nums = 0;
	instance->verify_item_index = ~0;
	instance->enroll_item_index = ~0;
	instance->item_data = NULL;
	instance->item_data_size = 0;
	memset(instance->item_header, 0xff, instance->item_size);

	if (instance->type < GKT_USER_TYPE_MAX) {
		GKT_ASSERT(instance->c_ops);
		if (instance->flags & USER_INSTANCE_F_ITEM_UPDATE_ENABLE)
			GKT_ASSERT(instance->c_ops->verify_update);
		GKT_ASSERT(instance->info);
		GKT_ASSERT(instance->user_maxnums > 0);
	}

	if (instance->info 
		&& (instance->info->magic != USER_TYPE_INFO_MAGIC))
	{
		user_reset_info(instance);
	}

	/* instance partition */
	retval = user_init_partition(instance);
	if (retval != GKT_SUCCESS)
		return retval;

	/* type private init */
	if (instance->c_ops && instance->c_ops->init) {
		retval = (*instance->c_ops->init)();
		if (retval != GKT_SUCCESS) {
			gkt_error("user_init: type(%u) init failed %d\n", 
				instance->type, retval);
			return retval;
		}
	}

	if (g_user_stash.c_partition_header
		&& USER_PARTITION_IS_VALID(&g_user_stash.c_partition_header->common, instance->type)
		&& (USER_PARTITION_ST_NORMAL == g_user_stash.c_partition_header->common.state))
	{
		retval = user_recycle(instance, 1);
		if (retval != GKT_SUCCESS)
			return retval;
	}
	else if (USER_PARTITION_IS_VALID(instance->c_partition_header, instance->type)) {
		if (USER_PARTITION_ST_RECYCLE_STARTED == instance->c_partition_header->state) {
			retval = user_recycle(instance, 0);
			if (retval != GKT_SUCCESS)
				return retval;
		}
	}
	else
		user_reset(instance);

	if ((instance->type < GKT_USER_TYPE_MAX) 
		&& (instance->flags & USER_INSTANCE_F_NEED_RETRIEVE))
	{
		user_retrieve(instance);
	}

	instance->flags |= USER_INSTANCE_F_INIT_OK;
	gkt_trace("user_init: flags = 0x%x\n", instance->flags);

	return GKT_SUCCESS;
}

